﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Audio;

namespace PokeTD
{
    class Player
    {
        public event TowerCreatedEventHandler TowerCreated;

        private string _name;
        private int _money;
        private int _lives;

        private Map map;
        private List<Tower> towers = new List<Tower>();
        private List<Texture2D> towerTextures;
        private List<Texture2D> bulletTextures;
        private List<Texture2D> gfxTextures;
        Texture2D rangetextures;

        private SoundEffect TPlace;

        /// <summary>
        /// Erstellt neuen Spieler
        /// </summary>
        /// <param name="map">Referenz zur Map</param>
        /// <param name="towerTextures">Liste von Tower-Texturen</param>
        /// <param name="name">Spieler-Name</param>
        /// <param name="money">Geldbetrag vom Spieler</param>
        /// <param name="lives">Anzahl Leben des Spielers</param>
        public Player(Map map, List<Texture2D> towerTextures, List<Texture2D> bulletTextures, List<Texture2D> gfxTextures, string name, int money, int lives, SoundEffect TPlaceSound, Texture2D rangetex)
        {
            this.map = map;
            this.towerTextures = towerTextures;
            this.bulletTextures = bulletTextures;
            this.gfxTextures = gfxTextures;
            this._name = name;
            this._money = money;
            this._lives = lives;
            this.rangetextures = rangetex;

            TPlace = TPlaceSound;
        }


        /// <summary>
        /// Tower auf der Map platzieren
        /// </summary>
        /// <param name="towerType">Typ des Towers</param>
        /// <param name="pos">Position des Towers</param>
        /// <returns>true, wenn Platzieren erfolgreich, sonst false.</returns>
        public bool PlaceTower(BuyTableEntry towerType, Vector2 pos)
        {
            if (!map.GetBlock((int)pos.X, (int)pos.Y).isWalkable() || pos == map.StartBlock.Position || pos == map.EndBlock.Position || (_money < towerType.Preis))
            {
                return false;
            }
            bool towerAtPath = map.DefaultPath.Contains(pos);
            map.GetBlock((int)pos.X, (int)pos.Y).HasTower = true;
            if (towerAtPath)
            {
                List<Vector2> path = map.pathToEndBlock(map.StartBlock, false);
                if (path == null)
                {
                    map.GetBlock((int)pos.X, (int)pos.Y).HasTower = false;
                    return false;
                }
                else
                {
                    map.DefaultPath = path;
                }
            }
            Tower t = new Tower(towerType.TowerID, towerTextures[towerType.TowerID], bulletTextures[towerType.TowerID]);
            TowerCreated(this, new TowerCreatedEventArgs(), t);
            t.Position = pos;
            towers.Add(t);
            TPlace.Play();

            return true;
        }

        public void RemoveTower(Tower t)
        {
            towers.Remove(t);
            map.GetBlock((int)t.Position.X, (int)t.Position.Y).HasTower = false;
            _money += t.Cost / 2;
        }

        public void UpgradeTower(Tower t)
        {
            if (t.UpgradeTo > -1)
            {
                Tower tw = new Tower(t.UpgradeTo, towerTextures[t.UpgradeTo], bulletTextures[t.UpgradeTo]);
                if (tw.Cost > _money) return;
                tw.Position = t.Position;
                TowerCreated(this, new TowerCreatedEventArgs(), tw);
                towers.Remove(t);
                towers.Add(tw);
                _money -= tw.Cost;
            }
        }

        public void Update(GameTime gameTime, Waves waves)
        {
            foreach (Tower tower in towers)
            {
                List<Creep> creeps = new List<Creep>();
                foreach (Wave wave in waves.WavesList)
                {
                    if (!wave.WaveCleared) creeps.AddRange(wave.Creeps);
                }
                if (tower.Target == null)
                {
                    tower.ChooseTarget(creeps);
                }
                tower.Update(gameTime, creeps, map.RenderRoot);
            }
        }
        public void Draw(SpriteBatch spriteBatch, BuyTableEntry towerChosen, bool active)
        {
            foreach (Tower tower in towers)
            {
                tower.Draw(spriteBatch, map.RenderRoot);
            }

            MouseState state = Mouse.GetState();
            if (state.X >= map.RenderRoot.X && state.X < map.Width * Block.BlockWidth + map.RenderRoot.X && state.Y >= map.RenderRoot.Y && state.Y < map.Height * Block.BlockHeight)
            {
                int x = (int)MathHelper.Clamp((state.X - (int)map.RenderRoot.X) / Block.BlockWidth, 0, map.Width - 1);
                int y = (int)MathHelper.Clamp((state.Y - (int)map.RenderRoot.Y) / Block.BlockHeight, 0, map.Height - 1);
                if (active)
                {
                    if (towerChosen != null)
                    {
                        Vector2 rect = map.RenderRoot + new Vector2(x, y) * new Vector2(Block.BlockWidth, Block.BlockHeight) - new Vector2((int)Math.Ceiling(towerChosen.Range) - 16, (int)Math.Ceiling(towerChosen.Range) - 16);
                        Rectangle destrec = new Rectangle((int)rect.X,(int)rect.Y,(int)Math.Ceiling(towerChosen.Range) * 2,(int)Math.Ceiling(towerChosen.Range) * 2);
                        spriteBatch.Draw(rangetextures, destrec, new Color(255, 255, 255, 50));
                        spriteBatch.Draw(towerTextures[towerChosen.TowerID], map.RenderRoot + new Vector2(x, y) * new Vector2(Block.BlockWidth, Block.BlockHeight), new Rectangle(64, 0, 32, 32), new Color(255, 255, 255, 50));
                    }

                    if (!map.GetBlock(x, y).isWalkable() || map.GetBlock(x, y).Position == map.StartBlock.Position || map.GetBlock(x, y).Position == map.EndBlock.Position)
                    {
                        spriteBatch.Draw(gfxTextures[1], map.RenderRoot + new Vector2(x, y) * new Vector2(Block.BlockWidth, Block.BlockHeight), Color.White);
                    }
                    else
                    {
                        spriteBatch.Draw(gfxTextures[0], map.RenderRoot + new Vector2(x, y) * new Vector2(Block.BlockWidth, Block.BlockHeight), Color.White);
                    }
                }
            }

            //Zeigt den kürzesten Pfad an. Kommt später weg.
#if DEBUG
            foreach (Vector2 v in map.DefaultPath)
            {
                spriteBatch.Draw(gfxTextures[0], map.RenderRoot + new Vector2(v.X, v.Y) * new Vector2(Block.BlockWidth, Block.BlockHeight), Color.White);
            }
#endif
        }


        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }
        public int Money
        {
            get { return _money; }
            set { _money = value; }
        }
        public int Lives
        {
            get { return _lives; }
            set 
            {
                if (value < _lives)
                {
                    Globals.MessageSystem.AddMessage("Du hast " + (_lives - value).ToString() + " Leben verloren!", Color.DarkRed);
                }
                _lives = value;
                if (_lives == 0)
                {
                    OnGameLost(this, new GameLostEventArgs());
                }
            }
        }
        public Map Map
        {
            get { return map; }
            set { map = value; }
        }
        public int KillCount;

        public int GetScore(float time)
        {
            int score = (int)(KillCount * 0.7 - towers.Count * 0.02 - time * 0.05 + Money * 0.2);
            if (score < 0) score = 0;
            return score;
        }
        public event GameLostEventHandler OnGameLost;
    }
}
